//    Copyright (C) Kherty.  All rights reserved.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using OpenLS.Core;
using OpenLS.Spreadsheet.Collections;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Internals;

namespace OpenLS.Spreadsheet
{
    public static class CellFormatter
    {
        public static void ParseValueAndFormat(string text, out object value, out Format formatting)
        {
            string s = text;
            if (s.Length == 0)
            {
                value = null;
                formatting = null;
            }
            bool b;
            if (bool.TryParse(s, out b))
            {
                value = b;
                formatting = null;
                return;
            }
            {
                double d;
                if (DoubleUtil.TryParsePossible(s, out d))
                {
                    value = d;
                    if (s.Contains("e") || s.Contains("E"))
                        formatting = Core.Formats.Scientific;
                    else
                        formatting = null;// Core.Formats.General; //\\ handle decimal count 
                    return;
                }
            }
            string s2 = s.Trim();
            {
                PercentValue percentValue = PercentValue.TryParse(s2);
                if (percentValue != null)
                {
                    value = percentValue.Value;

                    // cell.Value = percentValue.Value;
                    formatting = percentValue.Format;
                    return;
                }
            }
            {
                if (s2.Length > 0)
                {
                    var currencySymbols = new[] {"$"}; //\\, "€", "£", "￥"};
                    foreach (string symbol in currencySymbols)
                    {
                        if (s2.StartsWith(symbol))
                        {
                            string ss = s2.Substring(1);
                            double d;
                            if (double.TryParse(ss, out d))
                            {
                                value = d;
                                formatting = Core.Formats.Currency; //\\ 
                                return;
                            }
                        }
                    }
                }
            }

            {
                string colon = DateTimeFormatInfo.CurrentInfo.TimeSeparator;
                if (s2.EndsWith(colon))
                {
                    s2 = s2 + "0";
                    DateTime dt;
                    if (DateTime.TryParse(s2, null, DateTimeStyles.NoCurrentDateDefault, out dt))
                    {
                        if (dt.Year == 1 && dt.Month == 1 && dt.Day == 1)
                        {
                            DateTime zd = Worksheet.ZeroDate;
                            dt = new DateTime(zd.Year, zd.Month, zd.Day, dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
                            value = dt;
                            //cell.Value = dt;
                            //\\NumberFormat = StandardFormats.TimeFormat;
                            if (dt.Second == 0)
                                formatting = Format.Parse("h:mm"); //\\id; check double ::
                            else
                            {
                                formatting = Format.Parse("h:mm:ss");
                            }
                            return;
                        }
                    }
                }
            }
            {
                DateTime dt;
                if (DateTime.TryParse(s2, null, DateTimeStyles.NoCurrentDateDefault, out dt))
                {
                    {
                        formatting = GetDateFormat(s2, dt);
                        if (formatting == null)
                        {
                            value = text;
                        }
                        else
                            value = dt;
                        return;
                    }
                }
            }

            value = text;
            formatting = null;
        }

        static Format GetDateFormat (string original, DateTime dt)
        {
            string l = original.ToLower();
            foreach (string s in new string[] { "d MMMM yyyy", "d-MMMM-yyyy", "d/MMMM/yyyy", "d MMM yyyy", "d-MMM-yyyy", "d/MMM/yyyy",
             "d MMMM yy", "d-MMMM-yy", "d/MMMM/yy", "d MMM yy", "d-MMM-yy", "d/MMM/yy"})
            {
                var v = dt.ToString(s).ToLower();
                if (v == l)
                    return Format.Parse("d-mmm-yy");
            }
            foreach (string s in new string[] { "M-dd-yyyy", "M/dd/yyyy", "M-dd-yy", "M/dd/yy", })
            {
                var v = dt.ToString(s).ToLower();
                if (v == l)
                    return Format.Parse("m/dd/yyyy");
            }
            foreach (string s in new string[] { "M-dd-yyyy", "M/dd/yyyy", "M-dd-yy", "M/dd/yy", })
            {
                var v = dt.ToString(s).ToLower();
                if (v == l)
                    return Format.Parse("m/dd/yyyy");
            }
            foreach (string s in new string[] { "MMMM yyyy", "MMMM-yyyy", "MMMM/yyyy", "MMM yyyy", "MMM-yyyy", "MMM/yyyy", })
            {
                var v = dt.ToString(s).ToLower();
                if (v == l)
                    return Format.Parse("mmm-yy");
            }
            foreach (var s in new string[] { "MMMM dd", "MMMM/dd", "MMMM dd", "MMM dd", "MMM/dd", "MMM-dd", "dd MMM", "dd-MMM", "dd/MMM", "dd MMMM", "dd-MMMM", "dd/MMMM" })
            {
                var v = dt.ToString(s).ToLower();
                if (v == l)
                    return Format.Parse("d-mmm");
            }
            
            foreach (string s in new string[] { "MMMM d yyyy", "MMMM d yy" })
            {
                var v = dt.ToString(s).ToLower();
                if (v == l)
                    return Format.Parse("mmm-d-yy");
            }
            foreach (string s in new string[] { "MMMM dd yyyy", "MMM dd yyyy" })
            {   var v = dt.ToString(s).ToLower();
            if (v == l)
                return null;
            }
            return Format.Parse(StringTable.DefaultDateTimeFormat);
        }
        private static IEnumerable<string> GetDateFormats()
        {
            yield return "General";
        }

        public static bool TrySetCurrency(Cell cell, string s2)
        {
            if (s2.Length > 0)
            {
                var currencySymbols = new[] {"$"}; //\\, "€", "£", "￥"};
                foreach (string symbol in currencySymbols)
                {
                    if (s2.StartsWith(symbol))
                    {
                        string ss = s2.Substring(1);
                        double d;
                        if (double.TryParse(ss, out d))
                        {
                            TrySetValueWithDataValidation(cell, d);
                            cell.Format = Core.Formats.Currency; //\\ 
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        public static bool TrySetTime(Cell cell, string s2)
        {
            string colon = DateTimeFormatInfo.CurrentInfo.TimeSeparator;
            if (s2.EndsWith(colon))
            {
                s2 = s2 + "0";
                DateTime dt;
                if (DateTime.TryParse(s2, null, DateTimeStyles.NoCurrentDateDefault, out dt))
                {
                    if (dt.Year == 1 && dt.Month == 1 && dt.Day == 1)
                    {
                        DateTime zd = Worksheet.ZeroDate;
                        dt = new DateTime(zd.Year, zd.Month, zd.Day, dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
                        TrySetValueWithDataValidation(cell, dt);
                        //cell.Value = dt;
                        //\\NumberFormat = StandardFormats.TimeFormat;
                        if (dt.Second == 0)
                            cell.Format = Format.Parse("h:mm"); //\\id; check double ::
                        else
                        {
                            cell.Format = Format.Parse("h:mm:ss");
                        }
                        return true;
                    }
                }
            }
            return false;
        }

        public static bool TrySetPercent(Cell cell, string s2)
        {
            PercentValue percentValue = PercentValue.TryParse(s2);
            if (percentValue != null)
            {
                TrySetValueWithDataValidation(cell, percentValue.Value);

                // cell.Value = percentValue.Value;
                cell.Format = percentValue.Format;
                return true;
            }
            return false;
        }

        public static void ApplyDateFormat(Cell cell, object value)
        {
            if (value is DateTime && cell.Format is GeneralFormat)
            {
                cell.Format = Format.Parse(StringTable.DefaultDateTimeFormat); //\\loc
            }
        }

        public static bool TrySetBool(Cell cell, string s)
        {
            bool b;
            if (bool.TryParse(s, out b))
            {
                TrySetValueWithDataValidation(cell, b);
//                cell.Value = b;
                SetGeneralFormat(cell);
                return true;
            }
            return false;
        }

        private static void SetGeneralFormat(Cell cell)
        {
        }

        //\\Nothing to do?
        public static bool TrySetDouble(Cell cell, string s)
        {
            double d;
            if (DoubleUtil.TryParsePossible(s, out d))
            {
                if (cell.Format != null && cell.Format is PercentFormat)
                {
                    double v = d;
                    if (Math.Floor(v) != v && cell.Format == Core.Formats.Percent)
                        cell.Format = Core.Formats.DecimalPercent;
                    TrySetValueWithDataValidation(cell, v);
                    return true;
                }
                TrySetValueWithDataValidation(cell, d);
                if (s.Contains("e") || s.Contains("E"))
                    cell.Format = Core.Formats.Scientific;
                else if (cell.Format == null)
                    cell.Format = Core.Formats.Number; //\\ handle decimal count 
                return true;
            }
            return false;
        }

        private static void TrySetValueWithDataValidation(Cell cell, object value)
        {
            foreach (
                DataValidation validation in Enumerable.Where(cell.Worksheet.DataValidations, dv => dv.AppliesTo(cell)))
            {
                if (!validation.IsValidValue(value, cell).GetValueOrDefault(true))
                    throw new DataValidationException(cell, validation);
            }
            cell.Value = value;
        }


        internal static bool TrySetReferencedFormat(Cell cell, Expression value)
        {
            if (value == null)
                return false;
            Format format = cell.Format;
            if (format != null)
            {
                if (!(format is GeneralFormat))
                    return false;
            }
            Format toSet = null;
            foreach (IDependencyNode node in value.GetPrecedents(new CellEvaluationContext(cell)))
            {
                var referencedCell = node as Cell;
                if (referencedCell == null)
                    continue;
                if (toSet == null)
                {
                    toSet = referencedCell.Format;
                    continue;
                }
                if (toSet != referencedCell.Format)
                    return false;
            }
            cell.Format = toSet;
            return true;
        }

        public static void SetDate(Cell cell, object value)
        {
            ApplyDateFormat(cell, value);
            TrySetValueWithDataValidation(cell, value);
        }
    }
}